package rpc.core.plugin.impl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.CollectionUtils;
import rpc.core.FlowMap;
import rpc.core.plugin.ServicePlugin;
import rpc.core.plugin.impl.proxy.DaoServiceProxy;
import rpc.core.plugin.impl.proxy.ServiceProxy;
import rpc.utils.ReflectionUtils;

/*********
 *
 *
 *@author 197
 *@date 2020/7/29 18:15
 *
 **/
public class DaoServicePlugin implements ServicePlugin {

  private static final Logger logger = LoggerFactory.getLogger(DaoServicePlugin.class);
  private final ApplicationContext context;
  private final Environment environment;
  private final Map<String, ServiceProxy> flowProxyMap = new HashMap<>();
  private int queueSize = 1000;
  private int threads = 0;
  private int maxThreads = 0;
  private ThreadPoolExecutor executor = null;
  private JdbcTemplate jdbcTemplate;
  private String beanName;

  public DaoServicePlugin(ApplicationContext context, Environment environment) {
    this.environment = environment;
    this.context = context;
  }

  public DaoServicePlugin setBeanName(String beanName) {
    this.beanName = beanName;
    return this;
  }

  @Override
  public void init() {
    List<String> classNames = getClassNameList(this.environment);
    if (CollectionUtils.isEmpty(classNames)) {
      return;
    }
    String name = Objects.nonNull(beanName) ? beanName : "db";
    DataSource dataSource = context.containsBean(name) ? (DataSource) context.getBean(name) : context.getBean(DataSource.class);
    jdbcTemplate = new JdbcTemplate(dataSource);
    threads = intConfig(environment, "rpc." + beanName + ".threads", 8);
    maxThreads = intConfig(environment, "rpc." + beanName + ".maxthreads", 8);
    queueSize = intConfig(environment, "rpc." + beanName + ".queue", 10000);
    executor = new ThreadPoolExecutor(threads, maxThreads, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(queueSize),
        new ThreadFactoryBuilder().setNameFormat("pool_dao_%d").build());
    executor.prestartCoreThread();
    classNames.forEach(cn -> {
      Class<?> aClass = ReflectionUtils.getClass(cn);
      Method[] methods = aClass.getMethods();
      String simpleName = aClass.getSimpleName().toLowerCase();
      Arrays.stream(methods).forEach(m -> {
        String flowName = simpleName + "." + m.getName().toLowerCase();
        Arrays.stream(m.getAnnotations())
            .filter(annotation -> annotation.annotationType().getName().contains("rpc.core.annotations.db"))
            .findFirst().ifPresent(annotation -> {
          ServiceProxy dbServiceProxy = new DaoServiceProxy().setFlowName(flowName)
              .setJdbcTemplate(jdbcTemplate).setMethod(m).setAnnotation(annotation).setExecutorService(executor);
          flowProxyMap.put(flowName, dbServiceProxy);
        });
      });
    });
  }

  @Override
  public Set<String> getFlowName() {
    return this.flowProxyMap.keySet();
  }

  @Override
  public void close() {
    if (Objects.nonNull(executor)) {
      this.executor.shutdown();
    }
  }

  @Override
  public Map<String, ServiceProxy> getFlowServiceProxy() {
    return this.flowProxyMap;
  }

  @Override
  public Map<String, Object> dump() {
    FlowMap flowMap = new FlowMap();
    flowMap.append("threads", threads);
    flowMap.append("maxThreads", maxThreads);
    return flowMap.toMap();
  }
}